#pragma once
#include <iostream>
#include <string>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "../comm/log.hpp"
#include "../comm/util.hpp"
#include "sand_box.hpp"

namespace ns_runner
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_sandbox;
    class Runner
    { 
        public:
        Runner()
        {}
        ~Runner()
        {}

        /***************
        * 该函数的返回值：
        * < 0,表示函数内部逻辑出错；
        * ==0表示可执行程序正常运行；
        * > 0表示可执行程序被信号异常终止

        * cpu_limit:s为单位
        * mem_limit:kb为单位
        *****************/
        static int Run(const std::string &file_name,std::string& IO_Input,std::string& IO_Output,const std::string& mode,int cpu_limit,int mem_limit)
        {
            /*
            程序运行结果：
            1. 代码跑完，结果正确
            2. 代码跑完，结果不正确
            3. 代码没跑完,无法正常运行
            Run模块需要考虑代码跑完情况下，结果正确与否吗？
            不需要，也无法完成：本质判断正确与否，是与正确答案进行比对的
            但是是否正常运行完毕：Run是要关心

            Run模块设计注意点：
            1. 可执行程序是谁
            2. 标准输入
            3. 标准输出：运行结果
            4. 标准错误：运行时相关错误信息
            */
           std::string file_exe = PathUtil::Exe(file_name);
           std::string _stdin;
           if(mode == "IO")
                _stdin = IO_Input;
           else
                _stdin = PathUtil::Stdin(file_name);
           std::string _stdout = PathUtil::Stdout(file_name);
           std::string _stderr = PathUtil::Stderr(file_name);
  
           umask(0);
           //打开相应三个文件
           int _stdin_fd = open(_stdin.c_str(),O_CREAT | O_RDONLY,0644);
           int _stdout_fd = open(_stdout.c_str(),O_CREAT | O_WRONLY,0644);
           int _stderr_fd = open(_stderr.c_str(),O_CREAT | O_WRONLY,0644);


           if(_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0)
           {
                LOG(LogLevel::ERROR) << "运行时,打开stdin,stdout,stderr中出现错误";
                return -1;
           }
           
           pid_t pid = fork();
           if(pid < 0)
           {
                LOG(LogLevel::ERROR) << "运行时创建子进程失败";
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                return -2;
           }
           else if(pid == 0)
           {
               //基于tracer-supervisor模式的sand_box
               int legal = 1;
               int status = run_tracer(file_exe,mem_limit,cpu_limit,_stdin_fd,_stdout_fd,_stderr_fd,legal);
               if(!legal)
                    raise(SIGSYS);
               else if(WIFSIGNALED(status))
                    raise(status & 0x7f);
               else
                    exit(0);
            }
           else
           {
                //父进程关闭不需要的子进程的file_fd
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                int status = 0;
                waitpid(pid,&status,0);
                LOG(LogLevel::INFO) << "正常启动运行可执行程序";
                return status & 0x7F;//status低16位中的高8位存储返回值，低8位中的最高位表示是否core dump,剩下的7位表示退出信号：0表示正常退出
           }
           return 0;
        }
    };
}